import React, { ReactNode, useState } from "react";
import * as auth from "auth-provider";
import { User } from "types/user";
import { http } from "utils/http";
import { useMounted } from "utils";

interface AuthForm {
  username: string;
  password: string;
}

/**
 * 使用当前的 token 请求用户信息并返回
 * @returns
 */
const bootstrapUser = async () => {
  let user = null;
  const token = auth.getToken();
  if (token) {
    const data = await http("me", { token });
    user = data.user;
  }
  return user;
};

/**
 * 创建一个 Context，使用泛型指定 Context 内各个元素的类型
 */
const AuthContext = React.createContext<
  | {
      user: User | null;
      login: (form: AuthForm) => Promise<void>;
      register: (form: AuthForm) => Promise<void>;
      logout: () => Promise<void>;
    }
  | undefined
>(undefined);

/**
 * Context 的名称，不是必须要写的，但建议写，便于排查问题
 */
AuthContext.displayName = "AuthContext";

/**
 * 申明组件管理 Context 中的各个元素，返回 Context.Provider 供使用。
 * @param param
 * @returns
 */
export const AuthProvider = ({ children }: { children: ReactNode }) => {
  const [user, setUser] = useState<User | null>(null);
  const login = (form: AuthForm) => auth.login(form).then(setUser);
  const register = (form: AuthForm) => auth.register(form).then(setUser);
  const logout = () => auth.logout().then(() => setUser(null));

  // AuthProvider 挂载时（每次刷新页面都会触发挂载）拿 token 换用户信息，实现保持登录状态
  useMounted(() => {
    bootstrapUser().then(setUser);
  });

  return (
    <AuthContext.Provider
      children={children}
      value={{ user, login, register, logout }}
    />
  );
};

/**
 * 封装为一个 Hook 便于使用 Context，这样可以不必在每个要用这个 Context 的地方使用 useContext() 来获取实例。
 * @returns
 */
export const useAuth = () => {
  const context = React.useContext(AuthContext);
  if (!context) {
    throw new Error("useAuth can not be used out of AuthProvider");
  }
  return context;
};
